#ifndef _DELEGATE_HPP_
#define _DELEGATE_HPP_

#include "jhdefs.hpp"
#include "event/abstractdelegate.hpp"

EVENT_NAMESPACE_BEGIN

template <class TObj, class TArgs, bool withSender = true> 
class TDelegate: public TAbstractDelegate<TArgs>
{
public:
	typedef void (TObj::*NotifyMethod)(const void*, TArgs&);

	TDelegate(TObj* obj, NotifyMethod method):
	_receiverObject(obj), 
		_receiverMethod(method)
	{
	}

	TDelegate(const TDelegate& delegate):
	TAbstractDelegate<TArgs>(delegate),
		_receiverObject(delegate._receiverObject),
		_receiverMethod(delegate._receiverMethod)
	{
	}

	~TDelegate()
	{
	}

	TDelegate& operator = (const TDelegate& delegate)
	{
		if (&delegate != this)
		{
			this->_receiverObject = delegate._receiverObject;
			this->_receiverMethod = delegate._receiverMethod;
		}
		return *this;
	}

	bool notify(const void* sender, TArgs& arguments)
	{
		CMutex::ScopedLock lock(_mutex);
		if (_receiverObject)
		{
			(_receiverObject->*_receiverMethod)(sender, arguments);
			return true;
		}
		else return false;
	}

	bool equals(const TAbstractDelegate<TArgs>& other) const
	{
		const TDelegate* pOtherDelegate = reinterpret_cast<const TDelegate*>(other.unwrap());
		return pOtherDelegate && _receiverObject == pOtherDelegate->_receiverObject && _receiverMethod == pOtherDelegate->_receiverMethod;
	}

	TAbstractDelegate<TArgs>* clone() const
	{
		return new TDelegate(*this);
	}

	void disable()
	{
		CMutex::ScopedLock lock(_mutex);
		_receiverObject = 0;
	}

protected:
	TObj*        _receiverObject;
	NotifyMethod _receiverMethod;
	CMutex        _mutex;

private:
	TDelegate();
};


template <class TObj, class TArgs> 
class TDelegate<TObj, TArgs, false>: public TAbstractDelegate<TArgs>
{
public:
	typedef void (TObj::*NotifyMethod)(TArgs&);

	TDelegate(TObj* obj, NotifyMethod method):
	_receiverObject(obj), 
		_receiverMethod(method)
	{
	}

	TDelegate(const TDelegate& delegate):
	TAbstractDelegate<TArgs>(delegate),
		_receiverObject(delegate._receiverObject),
		_receiverMethod(delegate._receiverMethod)
	{
	}

	~TDelegate()
	{
	}

	TDelegate& operator = (const TDelegate& delegate)
	{
		if (&delegate != this)
		{
			this->_pTarget        = delegate._pTarget;
			this->_receiverObject = delegate._receiverObject;
			this->_receiverMethod = delegate._receiverMethod;
		}
		return *this;
	}

	bool notify(const void*, TArgs& arguments)
	{
		CMutex::ScopedLock lock(_mutex);
		if (_receiverObject)
		{
			(_receiverObject->*_receiverMethod)(arguments);
			return true;
		}
		else return false;
	}

	bool equals(const TAbstractDelegate<TArgs>& other) const
	{
		const TDelegate* pOtherDelegate = reinterpret_cast<const TDelegate*>(other.unwrap());
		return pOtherDelegate && _receiverObject == pOtherDelegate->_receiverObject && _receiverMethod == pOtherDelegate->_receiverMethod;
	}

	TAbstractDelegate<TArgs>* clone() const
	{
		return new TDelegate(*this);
	}

	void disable()
	{
		CMutex::ScopedLock lock(_mutex);
		_receiverObject = 0;
	}

protected:
	TObj*        _receiverObject;
	NotifyMethod _receiverMethod;
	CMutex        _mutex;

private:
	TDelegate();
};


template <class T>
class TExpire : public TAbstractDelegate<T>
{
public:
	TExpire(const TAbstractDelegate<T>& p, CTimeStamp::TimeDiff expireMillisecs):
	  _pDelegate(p.clone()), 
		  _expire(expireMillisecs*1000)
	  {
	  }

	  TExpire(const TExpire& expire):
	  TAbstractDelegate<T>(expire),
		  _pDelegate(expire._pDelegate->clone()),
		  _expire(expire._expire),
		  _creationTime(expire._creationTime)
	  {
	  }

	  ~TExpire()
	  {
		  delete _pDelegate;
	  }

	  TExpire& operator = (const TExpire& expire)
	  {
		  if (&expire != this)
		  {
			  delete this->_pDelegate;
			  this->_pDelegate    = expire._pDelegate->clone();
			  this->_expire       = expire._expire;
			  this->_creationTime = expire._creationTime;
			  this->_pTarget = expire._pTarget;
		  }
		  return *this;
	  }

	  bool notify(const void* sender, T& arguments)
	  {
		  if (!expired())
			  return this->_pDelegate->notify(sender, arguments);
		  else
			  return false;
	  }

	  bool equals(const TAbstractDelegate<T>& other) const
	  {
		  return other.equals(*_pDelegate);
	  }

	  TAbstractDelegate<T>* clone() const
	  {
		  return new TExpire(*this);
	  }

	  void disable()
	  {
		  _pDelegate->disable();
	  }

	  const TAbstractDelegate<T>* unwrap() const
	  {
		  return this->_pDelegate;
	  }

protected:
	bool expired() const
	{
		return _creationTime.isElapsed(_expire);
	}

	TAbstractDelegate<T>* _pDelegate;
	CTimeStamp::TimeDiff _expire;
	CTimeStamp _creationTime;

private:
	TExpire();
};


template <>
class TExpire<void>: public TAbstractDelegate<void>
{
public:
	TExpire(const TAbstractDelegate<void>& p, CTimeStamp::TimeDiff expireMillisecs):
	  _pDelegate(p.clone()), 
		  _expire(expireMillisecs*1000)
	  {
	  }

	  TExpire(const TExpire& expire):
	  TAbstractDelegate<void>(expire),
		  _pDelegate(expire._pDelegate->clone()),
		  _expire(expire._expire),
		  _creationTime(expire._creationTime)
	  {
	  }

	  ~TExpire()
	  {
		  delete _pDelegate;
	  }

	  TExpire& operator = (const TExpire& expire)
	  {
		  if (&expire != this)
		  {
			  delete this->_pDelegate;
			  this->_pDelegate    = expire._pDelegate->clone();
			  this->_expire       = expire._expire;
			  this->_creationTime = expire._creationTime;
			  //this->_pTarget = expire._pTarget;
		  }
		  return *this;
	  }

	  bool notify(const void* sender)
	  {
		  if (!expired())
			  return this->_pDelegate->notify(sender);
		  else
			  return false;
	  }

	  bool equals(const TAbstractDelegate<void>& other) const
	  {
		  return other.equals(*_pDelegate);
	  }

	  TAbstractDelegate<void>* clone() const
	  {
		  return new TExpire(*this);
	  }

	  void disable()
	  {
		  _pDelegate->disable();
	  }

	  const TAbstractDelegate<void>* unwrap() const
	  {
		  return this->_pDelegate;
	  }

protected:
	bool expired() const
	{
		return _creationTime.isElapsed(_expire);
	}

	TAbstractDelegate<void>* _pDelegate;
	CTimeStamp::TimeDiff _expire;
	CTimeStamp _creationTime;

private:
	TExpire();
};


template <class TArgs, bool hasSender = true, bool senderIsConst = true> 
class FunctionDelegate: public TAbstractDelegate<TArgs>
	/// Wraps a freestanding function or static member function 
	/// for use as a Delegate.
{
public:
	typedef void (*NotifyFunction)(const void*, TArgs&);

	FunctionDelegate(NotifyFunction function):
	_function(function)
	{
	}

	FunctionDelegate(const FunctionDelegate& delegate):
	TAbstractDelegate<TArgs>(delegate),
		_function(delegate._function)
	{
	}

	~FunctionDelegate()
	{
	}

	FunctionDelegate& operator = (const FunctionDelegate& delegate)
	{
		if (&delegate != this)
		{
			this->_function = delegate._function;
		}
		return *this;
	}

	bool notify(const void* sender, TArgs& arguments)
	{
		CMutex::ScopedLock lock(_mutex);
		if (_function)
		{
			(*_function)(sender, arguments);
			return true;
		}
		else return false;
	}

	bool equals(const TAbstractDelegate<TArgs>& other) const
	{
		const FunctionDelegate* pOtherDelegate = dynamic_cast<const FunctionDelegate*>(other.unwrap());
		return pOtherDelegate && _function == pOtherDelegate->_function;
	}

	TAbstractDelegate<TArgs>* clone() const
	{
		return new FunctionDelegate(*this);
	}

	void disable()
	{
		CMutex::ScopedLock lock(_mutex);
		_function = 0;
	}

protected:
	NotifyFunction _function;
	CMutex _mutex;

private:
	FunctionDelegate();
};


template <class TArgs> 
class FunctionDelegate<TArgs, true, false>: public TAbstractDelegate<TArgs>
{
public:
	typedef void (*NotifyFunction)(void*, TArgs&);

	FunctionDelegate(NotifyFunction function):
	_function(function)
	{
	}

	FunctionDelegate(const FunctionDelegate& delegate):
	TAbstractDelegate<TArgs>(delegate),
		_function(delegate._function)
	{
	}

	~FunctionDelegate()
	{
	}

	FunctionDelegate& operator = (const FunctionDelegate& delegate)
	{
		if (&delegate != this)
		{
			this->_function = delegate._function;
		}
		return *this;
	}

	bool notify(const void* sender, TArgs& arguments)
	{
		CMutex::ScopedLock lock(_mutex);
		if (_function)
		{
			(*_function)(const_cast<void*>(sender), arguments);
			return true;
		}
		else return false;
	}

	bool equals(const TAbstractDelegate<TArgs>& other) const
	{
		const FunctionDelegate* pOtherDelegate = dynamic_cast<const FunctionDelegate*>(other.unwrap());
		return pOtherDelegate && _function == pOtherDelegate->_function;
	}

	TAbstractDelegate<TArgs>* clone() const
	{
		return new FunctionDelegate(*this);
	}

	void disable()
	{
		CMutex::ScopedLock lock(_mutex);
		_function = 0;
	}

protected:
	NotifyFunction _function;
	CMutex _mutex;

private:
	FunctionDelegate();
};


template <class TArgs, bool senderIsConst> 
class FunctionDelegate<TArgs, false, senderIsConst>: public TAbstractDelegate<TArgs>
{
public:
	typedef void (*NotifyFunction)(TArgs&);

	FunctionDelegate(NotifyFunction function):
	_function(function)
	{
	}

	FunctionDelegate(const FunctionDelegate& delegate):
	TAbstractDelegate<TArgs>(delegate),
		_function(delegate._function)
	{
	}

	~FunctionDelegate()
	{
	}

	FunctionDelegate& operator = (const FunctionDelegate& delegate)
	{
		if (&delegate != this)
		{
			this->_function = delegate._function;
		}
		return *this;
	}

	bool notify(const void* /*sender*/, TArgs& arguments)
	{
		CMutex::ScopedLock lock(_mutex);
		if (_function)
		{
			(*_function)(arguments);
			return true; 
		}
		else return false;
	}

	bool equals(const TAbstractDelegate<TArgs>& other) const
	{
		const FunctionDelegate* pOtherDelegate = dynamic_cast<const FunctionDelegate*>(other.unwrap());
		return pOtherDelegate && _function == pOtherDelegate->_function;
	}

	TAbstractDelegate<TArgs>* clone() const
	{
		return new FunctionDelegate(*this);
	}

	void disable()
	{
		CMutex::ScopedLock lock(_mutex);
		_function = 0;
	}

protected:
	NotifyFunction _function;
	CMutex _mutex;

private:
	FunctionDelegate();
};


template <> 
class FunctionDelegate<void, true, true>: public TAbstractDelegate<void>
	/// Wraps a freestanding function or static member function 
	/// for use as a Delegate.
{
public:
	typedef void (*NotifyFunction)(const void*);

	FunctionDelegate(NotifyFunction function):
	_function(function)
	{
	}

	FunctionDelegate(const FunctionDelegate& delegate):
	TAbstractDelegate<void>(delegate),
		_function(delegate._function)
	{
	}

	~FunctionDelegate()
	{
	}

	FunctionDelegate& operator = (const FunctionDelegate& delegate)
	{
		if (&delegate != this)
		{
			this->_function = delegate._function;
		}
		return *this;
	}

	bool notify(const void* sender)
	{
		CMutex::ScopedLock lock(_mutex);
		if (_function)
		{
			(*_function)(sender);
			return true;
		}
		else return false;
	}

	bool equals(const TAbstractDelegate<void>& other) const
	{
		const FunctionDelegate* pOtherDelegate = dynamic_cast<const FunctionDelegate*>(other.unwrap());
		return pOtherDelegate && _function == pOtherDelegate->_function;
	}

	TAbstractDelegate<void>* clone() const
	{
		return new FunctionDelegate(*this);
	}

	void disable()
	{
		CMutex::ScopedLock lock(_mutex);
		_function = 0;
	}

protected:
	NotifyFunction _function;
	CMutex _mutex;

private:
	FunctionDelegate();
};


template <> 
class FunctionDelegate<void, true, false>: public TAbstractDelegate<void>
{
public:
	typedef void (*NotifyFunction)(void*);

	FunctionDelegate(NotifyFunction function):
	_function(function)
	{
	}

	FunctionDelegate(const FunctionDelegate& delegate):
	TAbstractDelegate<void>(delegate),
		_function(delegate._function)
	{
	}

	~FunctionDelegate()
	{
	}

	FunctionDelegate& operator = (const FunctionDelegate& delegate)
	{
		if (&delegate != this)
		{
			this->_function = delegate._function;
		}
		return *this;
	}

	bool notify(const void* sender)
	{
		CMutex::ScopedLock lock(_mutex);
		if (_function)
		{
			(*_function)(const_cast<void*>(sender));
			return true;
		}
		else return false;
	}

	bool equals(const TAbstractDelegate<void>& other) const
	{
		const FunctionDelegate* pOtherDelegate = dynamic_cast<const FunctionDelegate*>(other.unwrap());
		return pOtherDelegate && _function == pOtherDelegate->_function;
	}

	TAbstractDelegate<void>* clone() const
	{
		return new FunctionDelegate(*this);
	}

	void disable()
	{
		CMutex::ScopedLock lock(_mutex);
		_function = 0;
	}

protected:
	NotifyFunction _function;
	CMutex _mutex;

private:
	FunctionDelegate();
};


template <bool senderIsConst> 
class FunctionDelegate<void, false, senderIsConst>: public TAbstractDelegate<void>
{
public:
	typedef void (*NotifyFunction)();

	FunctionDelegate(NotifyFunction function):
	_function(function)
	{
	}

	FunctionDelegate(const FunctionDelegate& delegate):
	TAbstractDelegate<void>(delegate),
		_function(delegate._function)
	{
	}

	~FunctionDelegate()
	{
	}

	FunctionDelegate& operator = (const FunctionDelegate& delegate)
	{
		if (&delegate != this)
		{
			this->_function = delegate._function;
		}
		return *this;
	}

	bool notify(const void* /*sender*/)
	{
		CMutex::ScopedLock lock(_mutex);
		if (_function)
		{
			(*_function)();
			return true; 
		}
		else return false;
	}

	bool equals(const TAbstractDelegate<void>& other) const
	{
		const FunctionDelegate* pOtherDelegate = dynamic_cast<const FunctionDelegate*>(other.unwrap());
		return pOtherDelegate && _function == pOtherDelegate->_function;
	}

	TAbstractDelegate<void>* clone() const
	{
		return new FunctionDelegate(*this);
	}

	void disable()
	{
		CMutex::ScopedLock lock(_mutex);
		_function = 0;
	}

protected:
	NotifyFunction _function;
	CMutex _mutex;

private:
	FunctionDelegate();
};


template <class TObj, class TArgs>
inline TDelegate<TObj, TArgs, true> delegate(TObj* pObj, void (TObj::*NotifyMethod)(const void*, TArgs&))
{
	return TDelegate<TObj, TArgs, true>(pObj, NotifyMethod);
}

template <class TObj, class TArgs>
inline TDelegate<TObj, TArgs, false> delegate(TObj* pObj, void (TObj::*NotifyMethod)(TArgs&))
{
	return TDelegate<TObj, TArgs, false>(pObj, NotifyMethod);
}


template <class TObj, class TArgs>
inline TExpire<TArgs> delegate(TObj* pObj, void (TObj::*NotifyMethod)(const void*, TArgs&), CTimeStamp::TimeDiff expireMillisecs)
{
	return TExpire<TArgs>(TDelegate<TObj, TArgs, true>(pObj, NotifyMethod), expireMillisecs);
}

template <class TObj, class TArgs>
inline TExpire<TArgs> delegate(TObj* pObj, void (TObj::*NotifyMethod)(TArgs&), CTimeStamp::TimeDiff expireMillisecs)
{
	return TExpire<TArgs>(TDelegate<TObj, TArgs, false>(pObj, NotifyMethod), expireMillisecs);
}

template <class TArgs>
inline TExpire<TArgs> delegate(void (*NotifyMethod)(const void*, TArgs&), CTimeStamp::TimeDiff expireMillisecs)
{
	return TExpire<TArgs>(FunctionDelegate<TArgs, true, true>(NotifyMethod), expireMillisecs);
}

template <class TArgs>
inline TExpire<TArgs> delegate(void (*NotifyMethod)(void*, TArgs&), CTimeStamp::TimeDiff expireMillisecs)
{
	return TExpire<TArgs>(FunctionDelegate<TArgs, true, false>(NotifyMethod), expireMillisecs);
}

template <class TArgs>
inline TExpire<TArgs> delegate(void (*NotifyMethod)(TArgs&), CTimeStamp::TimeDiff expireMillisecs)
{
	return TExpire<TArgs>(FunctionDelegate<TArgs, false>(NotifyMethod), expireMillisecs);
}

template <class TArgs>
inline FunctionDelegate<TArgs, true, true> delegate(void (*NotifyMethod)(const void*, TArgs&))
{
	return FunctionDelegate<TArgs, true, true>(NotifyMethod);
}

template <class TArgs>
inline FunctionDelegate<TArgs, true, false> delegate(void (*NotifyMethod)(void*, TArgs&))
{
	return FunctionDelegate<TArgs, true, false>(NotifyMethod);
}

template <class TArgs>
inline FunctionDelegate<TArgs, false> delegate(void (*NotifyMethod)(TArgs&))
{
	return FunctionDelegate<TArgs, false>(NotifyMethod);
}


template <class TObj> 
class TDelegate<TObj,void,true>: public TAbstractDelegate<void>
{
public:
	typedef void (TObj::*NotifyMethod)(const void*);

	TDelegate(TObj* obj, NotifyMethod method):
	_receiverObject(obj), 
		_receiverMethod(method)
	{
	}

	TDelegate(const TDelegate& delegate):
	TAbstractDelegate<void>(delegate),
		_receiverObject(delegate._receiverObject),
		_receiverMethod(delegate._receiverMethod)
	{
	}

	~TDelegate()
	{
	}

	TDelegate& operator = (const TDelegate& delegate)
	{
		if (&delegate != this)
		{
			this->_receiverObject = delegate._receiverObject;
			this->_receiverMethod = delegate._receiverMethod;
		}
		return *this;
	}

	bool notify(const void* sender)
	{
		CMutex::ScopedLock lock(_mutex);
		if (_receiverObject)
		{
			(_receiverObject->*_receiverMethod)(sender);
			return true;
		}
		else return false;
	}

	bool equals(const TAbstractDelegate<void>& other) const
	{
		const TDelegate* pOtherDelegate = reinterpret_cast<const TDelegate*>(other.unwrap());
		return pOtherDelegate && _receiverObject == pOtherDelegate->_receiverObject && _receiverMethod == pOtherDelegate->_receiverMethod;
	}

	TAbstractDelegate<void>* clone() const
	{
		return new TDelegate(*this);
	}

	void disable()
	{
		CMutex::ScopedLock lock(_mutex);
		_receiverObject = 0;
	}

protected:
	TObj*        _receiverObject;
	NotifyMethod _receiverMethod;
	CMutex        _mutex;

private:
	TDelegate();
};


template <class TObj> 
class TDelegate<TObj, void, false>: public TAbstractDelegate<void>
{
public:
	typedef void (TObj::*NotifyMethod)();

	TDelegate(TObj* obj, NotifyMethod method):
	_receiverObject(obj), 
		_receiverMethod(method)
	{
	}

	TDelegate(const TDelegate& delegate):
	TAbstractDelegate<void>(delegate),
		_receiverObject(delegate._receiverObject),
		_receiverMethod(delegate._receiverMethod)
	{
	}

	~TDelegate()
	{
	}

	TDelegate& operator = (const TDelegate& delegate)
	{
		if (&delegate != this)
		{
			this->_pTarget        = delegate._pTarget;
			this->_receiverObject = delegate._receiverObject;
			this->_receiverMethod = delegate._receiverMethod;
		}
		return *this;
	}

	bool notify(const void*)
	{
		CMutex::ScopedLock lock(_mutex);
		if (_receiverObject)
		{
			(_receiverObject->*_receiverMethod)();
			return true;
		}
		else return false;
	}

	bool equals(const TAbstractDelegate<void>& other) const
	{
		const TDelegate* pOtherDelegate = reinterpret_cast<const TDelegate*>(other.unwrap());
		return pOtherDelegate && _receiverObject == pOtherDelegate->_receiverObject && _receiverMethod == pOtherDelegate->_receiverMethod;
	}

	TAbstractDelegate<void>* clone() const
	{
		return new Delegate(*this);
	}

	void disable()
	{
		CMutex::ScopedLock lock(_mutex);
		_receiverObject = 0;
	}

protected:
	TObj*        _receiverObject;
	NotifyMethod _receiverMethod;
	CMutex        _mutex;

private:
	TDelegate();
};


template <class TObj>
inline TDelegate<TObj, void, true> delegate(TObj* pObj, void (TObj::*NotifyMethod)(const void*))
{
	return TDelegate<TObj, void, true>(pObj, NotifyMethod);
}

template <class TObj>
inline TDelegate<TObj, void, false> delegate(TObj* pObj, void (TObj::*NotifyMethod)())
{
	return TDelegate<TObj, void, false>(pObj, NotifyMethod);
}


template <class TObj>
inline TExpire<void> delegate(TObj* pObj, void (TObj::*NotifyMethod)(const void*), CTimeStamp::TimeDiff expireMillisecs)
{
	return TExpire<void>(TDelegate<TObj, void, true>(pObj, NotifyMethod), expireMillisecs);
}

template <class TObj>
inline TExpire<void> delegate(TObj* pObj, void (TObj::*NotifyMethod)(), CTimeStamp::TimeDiff expireMillisecs)
{
	return TExpire<void>(TDelegate<TObj, void, false>(pObj, NotifyMethod), expireMillisecs);
}

inline TExpire<void> delegate(void (*NotifyMethod)(const void*), CTimeStamp::TimeDiff expireMillisecs)
{
	return TExpire<void>(FunctionDelegate<void, true, true>(NotifyMethod), expireMillisecs);
}

inline TExpire<void> delegate(void (*NotifyMethod)(void*), CTimeStamp::TimeDiff expireMillisecs)
{
	return TExpire<void>(FunctionDelegate<void, true, false>(NotifyMethod), expireMillisecs);
}

inline TExpire<void> delegate(void (*NotifyMethod)(), CTimeStamp::TimeDiff expireMillisecs)
{
	return TExpire<void>(FunctionDelegate<void, false>(NotifyMethod), expireMillisecs);
}

inline FunctionDelegate<void, true, true> delegate(void (*NotifyMethod)(const void*))
{
	return FunctionDelegate<void, true, true>(NotifyMethod);
}

inline FunctionDelegate<void, true, false> delegate(void (*NotifyMethod)(void*))
{
	return FunctionDelegate<void, true, false>(NotifyMethod);
}

inline FunctionDelegate<void, false> delegate(void (*NotifyMethod)())
{
	return FunctionDelegate<void, false>(NotifyMethod);
}
EVENT_NAMESPACE_END

#endif
